﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml.Serialization;

namespace Fantacid303.ProcessAffinityControl.Config
{
	public class ConfigurationChangedEventArgs : EventArgs
	{
		public ConfigurationChangedEventArgs(Configuration configuration)
		{
			this.Configuration = configuration;	
		}


		public Configuration Configuration
		{ get; private set;}
		
	}

	public class ConfigurationManager
	{
		private FileSystemWatcher configFileWatcher;
		private readonly string configFileName;
		private Mutex configFileAccessMutex = new Mutex(false, "{8350B676-6A19-4204-ACD0-6C04069E4F37}");

		public Configuration Configuration
		{
			get;
			private set;
		}

		public ConfigurationManager()
		{
			var directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			this.configFileName = Path.Combine(directory, "ProcessAffinityControl.configuration");

			this.configFileWatcher = new FileSystemWatcher(directory, "ProcessAffinityControl.configuration");
			this.configFileWatcher.EnableRaisingEvents = true;
			this.configFileWatcher.Changed += ConfigFileWatcher_Changed;
		}

		public void CreateDefaultConfiguration()
		{
			var defaultConfig = this.GetDefaultConfiguration();
			this.Save(defaultConfig);
			this.ReadConfig();
		}

		private Configuration GetDefaultConfiguration()
		{
			// default configuration
			var config = new Configuration();

			config.Interval = 60;
			config.ProcessIgnoreList.AddRange(new[] { "System", "Idle" });

			var entry1 = new ConfigurationEntry {Name = "Default", IsActive=true};
			entry1.Rules.Add(new ConfigurationRule { ProcessName = "*", AffinityMask=0, ProcessorAffinityType= Common.ProcessorAffinityType.AllCores });

			var entry2 = new ConfigurationEntry {Name = "Cubase"};
			entry2.Rules.Add(new ConfigurationRule { ProcessName = "Cubase4", AffinityMask = 0, ProcessorAffinityType = Common.ProcessorAffinityType.RealCores });
			entry2.Rules.Add(new ConfigurationRule { ProcessName = "Cubase5", AffinityMask = 0, ProcessorAffinityType = Common.ProcessorAffinityType.RealCores });
			entry2.Rules.Add(new ConfigurationRule { ProcessName = "*", AffinityMask = 0, ProcessorAffinityType = Common.ProcessorAffinityType.ThreadingCores });

			config.ConfigurationEntries.Add(entry1);
			config.ConfigurationEntries.Add(entry2);

			return config;
		}



		public void ReadConfig()
		{
			try
			{
				configFileAccessMutex.WaitOne(Timeout.Infinite);

				var serializer = new XmlSerializer(typeof(Configuration));
				try
				{
					using (var stream = File.OpenRead(this.configFileName))
					{
						this.Configuration = (Configuration)serializer.Deserialize(stream);
					}
				}
				catch (FileNotFoundException)
				{
					throw;
				}
/*				catch (System.Exception ex)
				{
					Thread.Sleep(1000);
					using (var stream = File.OpenRead(this.configFileName))
					{
						this.Configuration = (Configuration)serializer.Deserialize(stream);
					}
				}
*/				

			}
			finally
			{
				configFileAccessMutex.ReleaseMutex();
			}


		}

		void ConfigFileWatcher_Changed(object sender, FileSystemEventArgs e)
		{
			if (this.ConfigurationChanged != null)
			{
				this.ReadConfig();
//				var configClone = (Configuration)this.Configuration.Clone();
				this.ConfigurationChanged(this, new ConfigurationChangedEventArgs(this.Configuration));
			}
		}

		public void Save()
		{
			this.Save(this.Configuration);
		}
        
		public void Save(Configuration configuration)
		{
			try
			{
				this.configFileAccessMutex.WaitOne(Timeout.Infinite);

				var serializer = new XmlSerializer(typeof(Configuration));

				if (System.IO.File.Exists(this.configFileName))
					System.IO.File.Delete(this.configFileName);
				using (var stream = File.CreateText(this.configFileName))
				{
					serializer.Serialize(stream, configuration);
				}
			}
			catch (Exception ex)
			{
				int asf = 0;
			}
			finally
			{
				this.configFileAccessMutex.ReleaseMutex();
			}

		}






		public event EventHandler<ConfigurationChangedEventArgs> ConfigurationChanged;

	}
}
